/*
 * TacticaPhilTheBest.java
 *
 * Created on 9 de marzo de 2007, 16:32
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package futbol.tacticas.concursantes2007.PhilTheBest;

//import futbol.Bola;
import futbol.General;
import futbol.tacticas.Comando;
import futbol.tacticas.ComandoGolpearBola;
import futbol.tacticas.ComandoIrA;
import futbol.tacticas.ComandoPase;
import futbol.tacticas.ComandoTiroAPuerta;
import futbol.tacticas.SituacionJugadores;
import futbol.tacticas.concursantes.Tactica;

import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;
import java.util.List;

/**
 *
 * @author jculubre
 */
public class TacticaPhilTheBest implements Tactica {
    
    private static final String NOMBRE = "Phil The Best";
    private static final int NUM_JUGADORES = 11;
    private static final int VEL_PELOTA_LIMITE_CONTROL = 15;
    private static final int DISTANCIA_RECORRIDO_CONTROLABLE = ((VEL_PELOTA_LIMITE_CONTROL*VEL_PELOTA_LIMITE_CONTROL)+VEL_PELOTA_LIMITE_CONTROL)/2;
    private static final int DISTANCIA_ALCANCE_BOLA = 10;
    private static final int ERROR_ALCANCE = 3;
    private static final boolean ERROR_ALCANCE_CONSTANTE = false;
    private static final int MARGEN_DUDA_VELOCIDAD = 0;
    private static final int FACTOR_FUERZA_PELOTA=3;
    
    private static final int JUGADORES_VAN_A_PELOTA_DEFENSA=2;
    private static final int JUGADORES_VAN_A_PELOTA_ATAQUE=JUGADORES_VAN_A_PELOTA_DEFENSA;
    
    /** Velocidad mínima con la que permito jugar (para evitar bloqueos de pelota) */
    private static final int VELOCIDAD_MINIMA = 12;
    
    /// Tipo de fuerza para el pase
    private static final int FUERZA_MAXIMA = 100;
    private static final int FUERZA_MEDIA = 35;
    
    /// Limites del campo
    private static final int LIMITE_X_MAX = 160;
    private static final int LIMITE_Y_MAX = 120;
    private static final int LIMITE_X_MIN = - LIMITE_X_MAX;
    private static final int LIMITE_Y_MIN = - LIMITE_Y_MAX;
    
    private static final int PROB_NULA = 0;
    private static final int PROB_BAJA = 1;
    private static final int PROB_MEDIA = 2;
    private static final int PROB_ALTA = 3;
    private static final int NUM_PREDICCIONES = 30;
    
    private static final double LIMITE_ANGULO_PARA_TIRO_SUP = 4*Math.PI/10;
    private static final double LIMITE_ANGULO_PARA_TIRO_INF = -4*Math.PI/10;
    
    private static final double ANGULO_SEPARACION_DEFENSAS = 2*Math.PI/10;
    
    private static final int PORTERO = 0;
    private static final int DEFENSA = 1;
    private static final int MEDIO = 2;
    private static final int DELANTERO = 3;
    private static final int LIBRE = 4;
    
    private static final int MAXIMA_FUERZA = 7;
    
    
    private static final ParametrosAnguloTiro ANGULOS_TIRO[] = {
        new ParametrosAnguloTiro(0, FUERZA_MAXIMA),                // 0
        new ParametrosAnguloTiro(0, FUERZA_MEDIA),                // 1
        
        new ParametrosAnguloTiro(1*Math.PI/10, FUERZA_MAXIMA),     // 2
        new ParametrosAnguloTiro(1*Math.PI/10, FUERZA_MEDIA),     // 3
        new ParametrosAnguloTiro(2*Math.PI/10, FUERZA_MAXIMA),     // 4
        new ParametrosAnguloTiro(2*Math.PI/10, FUERZA_MEDIA),     // 5
        new ParametrosAnguloTiro(3*Math.PI/10, FUERZA_MAXIMA),     // 6
        new ParametrosAnguloTiro(3*Math.PI/10, FUERZA_MEDIA),     // 7
        new ParametrosAnguloTiro(4*Math.PI/10, FUERZA_MAXIMA),     // 8
        new ParametrosAnguloTiro(4*Math.PI/10, FUERZA_MEDIA),     // 9
        new ParametrosAnguloTiro(5*Math.PI/10, FUERZA_MAXIMA),     // 10
        new ParametrosAnguloTiro(5*Math.PI/10, FUERZA_MEDIA),     // 11
        new ParametrosAnguloTiro(6*Math.PI/10, FUERZA_MAXIMA),     // 12
        new ParametrosAnguloTiro(6*Math.PI/10, FUERZA_MEDIA),     // 13
        new ParametrosAnguloTiro(7*Math.PI/10, FUERZA_MAXIMA),     // 14
        new ParametrosAnguloTiro(7*Math.PI/10, FUERZA_MEDIA),     // 15
        new ParametrosAnguloTiro(8*Math.PI/10, FUERZA_MAXIMA),     // 16
        new ParametrosAnguloTiro(8*Math.PI/10, FUERZA_MEDIA),     // 17
        new ParametrosAnguloTiro(9*Math.PI/10, FUERZA_MAXIMA),     // 18
        new ParametrosAnguloTiro(9*Math.PI/10, FUERZA_MEDIA),     // 19
        
        new ParametrosAnguloTiro(10*Math.PI/10, FUERZA_MAXIMA),     // 20
        new ParametrosAnguloTiro(10*Math.PI/10, FUERZA_MEDIA),     // 21
        
        new ParametrosAnguloTiro(-9*Math.PI/10, FUERZA_MAXIMA),     // 22
        new ParametrosAnguloTiro(-9*Math.PI/10, FUERZA_MEDIA),     // 23
        new ParametrosAnguloTiro(-8*Math.PI/10, FUERZA_MAXIMA),     // 24
        new ParametrosAnguloTiro(-8*Math.PI/10, FUERZA_MEDIA),     // 25
        new ParametrosAnguloTiro(-7*Math.PI/10, FUERZA_MAXIMA),     // 26
        new ParametrosAnguloTiro(-7*Math.PI/10, FUERZA_MEDIA),     // 27
        new ParametrosAnguloTiro(-6*Math.PI/10, FUERZA_MAXIMA),     // 28
        new ParametrosAnguloTiro(-6*Math.PI/10, FUERZA_MEDIA),     // 29
        new ParametrosAnguloTiro(-5*Math.PI/10, FUERZA_MAXIMA),     // 30
        new ParametrosAnguloTiro(-5*Math.PI/10, FUERZA_MEDIA),     // 31
        new ParametrosAnguloTiro(-4*Math.PI/10, FUERZA_MAXIMA),     // 32
        new ParametrosAnguloTiro(-4*Math.PI/10, FUERZA_MEDIA),     // 33
        new ParametrosAnguloTiro(-3*Math.PI/10, FUERZA_MAXIMA),     // 34
        new ParametrosAnguloTiro(-3*Math.PI/10, FUERZA_MEDIA),     // 35
        new ParametrosAnguloTiro(-2*Math.PI/10, FUERZA_MAXIMA),     // 36
        new ParametrosAnguloTiro(-2*Math.PI/10, FUERZA_MEDIA),     // 37
        new ParametrosAnguloTiro(-1*Math.PI/10, FUERZA_MAXIMA),     // 38
        new ParametrosAnguloTiro(-1*Math.PI/10, FUERZA_MEDIA)     // 39
    };
    
    
    /** Orden de prioridad de los pases
     * [i] -> Grupo
     * [i][j] -> Pase
     */
    /*
    private static final int PRIORIDADES_TIRO[][] = {
        {0, 2, 4, 36, 38},
        {6, 8, 10, 34, 32},
        {12, 14, 30, 28, 26},
        {16, 18, 21, 22, 24}
    };
     */
    
    private static final int PRIORIDADES_TIRO[][] = {
        {0, 1, 2, 3, 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37, 38, 39}
    };
    
    /*{0, 2, 4, 37, 39},
        {1, 3, 5, 36, 38},
        {6, 7, 8, 9, 32, 33, 34, 35},
        {10, 11, 12, 13, 28, 29, 30, 31},
        {14, 15, 16, 17, 24, 25, 26, 27},
        {18, 19, 20, 21, 22, 23},*/
    
    private static final int UMBRAL_PROBABILIDAD[] = {
        PROB_BAJA,
        PROB_BAJA,
        PROB_BAJA,
        PROB_BAJA,
        PROB_BAJA,
        PROB_BAJA
    };
    
    private static final Point PUNTOS_DE_GOL[] = {new Point(LIMITE_X_MAX, 0),new Point(LIMITE_X_MAX, -20),new Point(LIMITE_X_MAX, 20)};
    
    /******************* A BORRAR - (es para pruebas) **********************/
    private static int dx=300, dy=210;
    //private static Point[] prediccionPelota = new Point[NUM_PREDICCIONES];
    private static List<PrediccionJugador> predicciones;
    private static PrediccionPelota[] prediccionPelotaStatic;
    private static PrediccionPelota[] prediccionPelotaDisparoStatic;
    public static void pintarPhil(java.awt.Graphics g) {
        // Pintado de la predicción de la bola ¡¡¡BORRAR!!!
        if (predicciones != null) {
            for (PrediccionPelota prediccionPelota: prediccionPelotaStatic) {
                if (prediccionPelota != null) {
                    Point puntoPelota = prediccionPelota.getCoordenadas();
                    int x=(int)(puntoPelota.getX()+dx-3);
                    int y=(int)(puntoPelota.getY()+dy-3);
                    g.setColor(Color.BLUE);
                    g.fillOval(x,y,6,6);
                    g.setColor(Color.BLUE);
                    g.drawOval(x,y,6,6);
                }
            }
            if (prediccionPelotaDisparoStatic!=null) for (PrediccionPelota prediccionPelota: prediccionPelotaDisparoStatic) {
                if (prediccionPelotaDisparoStatic != null) {
                    Point puntoPelota = prediccionPelota.getCoordenadas();
                    int x=(int)(puntoPelota.getX()+dx-3);
                    int y=(int)(puntoPelota.getY()+dy-3);
                    g.setColor(Color.PINK);
                    g.fillOval(x,y,6,6);
                    g.setColor(Color.PINK);
                    g.drawOval(x,y,6,6);
                }
            }
            
            for (int i=0; i< predicciones.size(); i++) {
                PrediccionJugador prediccion = predicciones.get(i);
                Point ptoIntercepcion = prediccion.getPuntoIntercepcionPelota();
                Point ptoJugador = prediccion.getPosicionJugador();
                Color colorJugador;
                switch (prediccion.getProbabilidadLlegarAPelota()) {
                    case PROB_ALTA:
                        colorJugador = Color.GREEN;
                        break;
                    case PROB_MEDIA:
                        colorJugador = Color.ORANGE;
                        break;
                    default:
                        colorJugador = Color.RED;
                }
                if (ptoIntercepcion != null) {
                    int x=(int)(ptoIntercepcion.getX()+dx-3);
                    int y=(int)(ptoIntercepcion.getY()+dy-3);
                    g.setColor(colorJugador);
                    g.fillOval(x,y,3,3);
                    g.setColor(Color.ORANGE);
                    g.drawOval(x,y,6,6);
                }
                if (ptoJugador != null) {
                    int x=(int)(ptoJugador.getX()+dx-3);
                    int y=(int)(ptoJugador.getY()+dy-3);
                    g.setColor(colorJugador);
                    g.fillOval(x,y,6,6);
                    g.setColor(colorJugador);
                    g.drawOval(x,y,6,6);
                }
                
            }
        }
    }
    /******************* FIN A BORRAR - (es para pruebas) **********************/
    
    private Partido partido = new Partido();
    
    
    public String getNombre() {
        return NOMBRE;
    }
    
    public Color getColor1() {
        return Color.PINK;
    }
    public Color getColor2() {
        return Color.BLACK;
    }
    public Color getColorNumero() {
        return Color.YELLOW;
    }
    
    public int getXInicial(int n) {
        return (int) partido.getMiEquipo().getXInicial(n); 
    }
    
    public int getYInicial(int n) {
        return (int) partido.getMiEquipo().getYInicial(n);
    }
    
    public int getFuerza(int n) {
        return (int) partido.getMiEquipo().getFuerza(n);
    }
    
    public static boolean esGol(double x, double y) {
        return esGolAFavor(x,y) || esGolEnContra(x,y);
    }
    
    public static boolean esGolAFavor(Point pto) {
        return esGolAFavor(pto.getX(), pto.getY());
    }
    
    public static boolean esGolEnContra(double x, double y) {
        return (x<=LIMITE_X_MIN && y>-30 && y<30);
    }
    
    public static boolean esGolAFavor(double x, double y) {
        return (x>=LIMITE_X_MAX && y>-30 && y<30);
    }
    
    public static boolean esGolEnContra(Point pto) {
        return esGolEnContra(pto.getX(), pto.getY());
    }
    
    protected boolean puedeGolpearBola(double xJugador, double yJugador, double xBola, double yBola, double velocidad) {
        double distanciaBola = General.getDistancia(xJugador,yJugador,xBola,yBola);
        //System.out.println("Velocidad -> "+velocidad);
        if (ERROR_ALCANCE_CONSTANTE)
            return (distanciaBola < (DISTANCIA_ALCANCE_BOLA+ERROR_ALCANCE));
        return (distanciaBola < (DISTANCIA_ALCANCE_BOLA+velocidad));
    }
    
    private class Trayectoria {
        public double x,y;
        public double angulo;
    }
    
    
    
    /**
     * Corrección de la trayectoria tal cual está en el framework... argps...
     * (lo del == lo he puesto yo)
     */
    protected Trayectoria corregirTrayectoria(double x, double y, double angulo) {
        if (x>=LIMITE_X_MAX){
            x=LIMITE_X_MAX;
            angulo=General.corregirAngulo(Math.PI-angulo);
        }
        if (x<=LIMITE_X_MIN){
            x=LIMITE_X_MIN;
            angulo=General.corregirAngulo(Math.PI-angulo);
        }
        if (y>=120.0){
            y=120.0;
            angulo=General.corregirAngulo(-angulo);
        }
        if (y<=-120.0){
            y=-120.0;
            angulo=General.corregirAngulo(-angulo);
        }
        Trayectoria tray = new Trayectoria();
        tray.x = x;
        tray.y = y;
        tray.angulo = angulo;
        return tray;
    }
    
    protected double limitarX(double x){
        if (x > LIMITE_X_MAX) x = 2*LIMITE_X_MAX - x;
        if (x < LIMITE_X_MIN) x = 2*LIMITE_X_MIN - x;
        return x;
    }
    protected double limitarY(double y){
        if (y > LIMITE_Y_MAX) y = 2*LIMITE_Y_MAX - y;
        if (y < LIMITE_Y_MIN) y = 2*LIMITE_Y_MIN - y;
        return y;
    }
    

    /**
     * Busca el angulo más cercano a <i>angulo</i> que se aleje de los <i>angulosNoValidos</i> 
     * de al menos <i>anguloSeparacion</i>
     *
     * @param angulo Angulo original
     * @param angulosNoValidos Angulo ya cogidos
     * @param anguloSeparacion Angulo mínimo de separación
     * @return el mejor angulo posible
     */
    protected double buscarMejorAngulo(double angulo, List<Double> angulosNoValidos, double anguloSeparacion, double anguloBase) {
        double anguloBuenoDer = buscarMejorAnguloDer(angulo, angulosNoValidos, anguloSeparacion);
        double anguloBuenoIzq = buscarMejorAnguloIzq(angulo, angulosNoValidos, anguloSeparacion);
        
        if (Math.abs(anguloBuenoDer-anguloBase) < Math.abs(anguloBuenoIzq-anguloBase))
            return anguloBuenoDer;
        
        return anguloBuenoIzq;
    }
    
    private double buscarMejorAnguloIzq(double angulo, List<Double> angulosNoValidos, double anguloSeparacion) {
        for (int i=0; i<angulosNoValidos.size(); i++) {
            if (Math.abs(angulo - angulosNoValidos.get(i)) < anguloSeparacion) {
                return buscarMejorAnguloIzq(angulo - anguloSeparacion, angulosNoValidos, anguloSeparacion);
            }
        }
        return angulo;
    }
    
    private double buscarMejorAnguloDer(double angulo, List<Double> angulosNoValidos, double anguloSeparacion) {
        for (int i=0; i<angulosNoValidos.size(); i++) {
            if (Math.abs(angulo - angulosNoValidos.get(i)) < anguloSeparacion) {
                return buscarMejorAnguloDer(angulo + anguloSeparacion, angulosNoValidos, anguloSeparacion);
            }
        }
        return angulo;
    }

    public Vector<Comando> getComandos(SituacionJugadores sj) {
        partido.actualizarPartido(sj);
        MiEquipo miEquipo = partido.getMiEquipo();
        return miEquipo.getEstrategia();
    }
    
    /**
     * Almacena los parámetros necesario de cada ángulo para el cálculo del
     * mejor "pase".
     */
    private static class ParametrosAnguloTiro {
        public double angulo;
        public int factorFuerza;
        
        public ParametrosAnguloTiro(double angulo, int factorFuerza) {
            this.angulo = angulo;
            this.factorFuerza = factorFuerza;
        }
    }
    
    
    /**
     * Predicciones de un jugador
     */
    private class PrediccionJugador {
        private int numeroDeJugador = -1;
        private double velocidadJugador;
        private boolean llegaALaPelota = false;
        private Point puntoIntercepcionPelota = new Point(0, 0);
        private double tiempoHastaPelota = Integer.MAX_VALUE;
        private int probabilidadLlegarAPelota = PROB_NULA;
        private Point posicionJugador = new Point(0, 0);
        
        public PrediccionJugador(int numeroDeJugador, double velocidad, double x, double y) {
            this.numeroDeJugador = numeroDeJugador;
            this.velocidadJugador = velocidad;
            setPosicionJugador(new Point((int) x, (int) y));
        }
        
        public boolean controlaLaPelota(Point pelota) {
            return puedeGolpearBola(posicionJugador.getX(), posicionJugador.getY(), pelota.getX(), pelota.getY(), velocidadJugador);
        }
        
        public int getNumeroDeJugador() {
            return numeroDeJugador;
        }
        
        public int getVelocidadJugador() {
            return (int) Math.round(velocidadJugador);
        }

        public int getFuerzaJugador() {
            return (10 - (int) Math.round(velocidadJugador));
        }
        
        public void llegaALaPelota(double x, double y, double tAlcance, int prob) {
            setLlegaALaPelota(true);
            setPuntoIntercepcionPelota(new Point((int) x,(int) y));
            setTiempoHastaPelota(tAlcance);
            setProbabilidadLlegarAPelota(prob);
        }
        
        public void noLlegaALaPelota() {
            setLlegaALaPelota(false);
            setPuntoIntercepcionPelota(new Point(0,0));
            setTiempoHastaPelota(Integer.MAX_VALUE);
            setProbabilidadLlegarAPelota(PROB_NULA);
        }
        
        
        public boolean llegaALaPelota() {
            return llegaALaPelota;
        }
        
        protected void setPosicionJugador(Point posicionJugador) {
            this.posicionJugador = posicionJugador;
        }
        public Point getPosicionJugador() {
            return posicionJugador;
        }
        
        protected void setProbabilidadLlegarAPelota(int probabilidadLlegarAPelota) {
            this.probabilidadLlegarAPelota = probabilidadLlegarAPelota;
        }
        
        protected int getProbabilidadLlegarAPelota() {
            return probabilidadLlegarAPelota;
        }
        
        protected void setLlegaALaPelota(boolean llegaALaPelota) {
            this.llegaALaPelota = llegaALaPelota;
        }
        
        public Point getPuntoIntercepcionPelota() {
            return puntoIntercepcionPelota;
        }
        protected void setPuntoIntercepcionPelota(Point puntoIntercepcionPelota) {
            this.puntoIntercepcionPelota = puntoIntercepcionPelota;
        }
        protected void setTiempoHastaPelota(double tiempoHastaPelota) {
            this.tiempoHastaPelota = tiempoHastaPelota;
        }
        public double getTiempoHastaPelota() {
            return tiempoHastaPelota;
        }
        
    }
    
    
    private abstract class UnEquipo {
        
        private Point[] colocacionActual;
        private SituacionJugadores sj;
        private List<PrediccionJugador> prediccionJugadorOrdenados = new ArrayList(NUM_JUGADORES);
        private List<PrediccionJugador> prediccionesTacticas = new ArrayList(NUM_JUGADORES);
        private int probabilidadDeInterceptarPelota = PROB_NULA;
        private Partido partido;
        
        public UnEquipo() {
        }
        
        protected Partido getPartido() {
            return partido;
        }
        
        /**
         * Actualiza el estado del equipo
         */
        public void actualizarEquipo(Partido partido, Point[] jugadores) {
            colocacionActual = jugadores;
            this.partido = partido;
            this.sj = partido.getSituacionJugadores();
            calcularTiemposHastaPelota(partido.getPelota());
        }
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        public abstract double getVelocidad(int jugador);
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        public abstract double getFuerza(int jugador);
        
        /**
         *Devuelve X de un jugador
         *@return x
         */
        public double getX(int jugador) {
            return (colocacionActual[jugador].getX());
        }
        
        /**
         *Devuelve Y de un jugador
         *@return y
         */
        public double getY(int jugador) {
            return (colocacionActual[jugador].getY());
        }
        
        public Point getPosicionJugador(int jugador) {
            return colocacionActual[jugador];
        }
        
        /**
         * Calculo de lo que tarda un jugador hasta las coordenadas
         * pasadas como parámetro.
         */
        public double getTiempoHastaPunto(int jugador, double xDest, double yDest) {
            double x = getX(jugador);
            double y = getY(jugador);
            
            double distancia = General.getDistancia(x, y, xDest, yDest);
            return distancia / getVelocidad(jugador);
        }
        
        /**
         * Calcula los tiempos hasta llegar a la pelota.
         *
         * @param prediccionPelota Punto por los que pasa la pelota
         * @param tInicial Tiempo a partir del cual calcular
         * @return Lista de jugadores ordenados por tiempo en interceptar la pelota
         */
        protected List<PrediccionJugador> calcularTiemposHastaPelota(PrediccionPelota prediccionPelota[], UnEquipo equipo, int tInicial) {
            List<PrediccionJugador> listaPrediccionJugadorOrdenados = new ArrayList(NUM_JUGADORES);
            
            for (int numJugador=0; numJugador<NUM_JUGADORES; numJugador++) {
                double x = equipo.getX(numJugador);
                double y = equipo.getY(numJugador);
                boolean interceptaLAPelotaSeguro = false;
                double velocidadJugador = equipo.getVelocidad(numJugador);
                for (int t=tInicial; (t<prediccionPelota.length) && !interceptaLAPelotaSeguro; t++) {
                    PrediccionJugador prediccionJugador = new PrediccionJugador(numJugador, velocidadJugador, x, y);
                    double xPelota = prediccionPelota[t].getX();
                    double yPelota = prediccionPelota[t].getY();
                    double distanciaAPelota = General.getDistancia(x, y, xPelota, yPelota);
                    int prob = prediccionPelota[t].getProbabilidadDePararPelota();
                    double tAlcance;
                    // comprobar si controla la pelota en t0
                    tAlcance = ((t == 0) && prediccionJugador.controlaLaPelota(new Point((int) xPelota,(int) yPelota)))?
                        0:
                        distanciaAPelota / (equipo.getVelocidad(numJugador)); // En otro caso
                    if (prob > PROB_NULA) {
                        if (tAlcance <= t) {
                            prediccionJugador.llegaALaPelota(xPelota, yPelota, tAlcance, prob);
                            anadirPrediccionOrdenada(listaPrediccionJugadorOrdenados, prediccionJugador);
                            // Si la probabilidad es alta, intercepto seguro la pelota y no tengo
                            // que buscar otros puntos de intercepción
                            interceptaLAPelotaSeguro = (prob >= PROB_ALTA);
                        }
                    }
                }
                if (!interceptaLAPelotaSeguro) {
                    PrediccionJugador prediccionJugador = new PrediccionJugador(numJugador, velocidadJugador, x, y);
                    prediccionJugador.noLlegaALaPelota();
                    anadirPrediccionOrdenada(listaPrediccionJugadorOrdenados, prediccionJugador);
                }
            }
            return listaPrediccionJugadorOrdenados;
        }
        
        
        
        /**
         * Calcula los tiempos hasta llegar a la pelota
         */
        protected void calcularTiemposHastaPelota(Pelota pelota) {
            prediccionJugadorOrdenados = calcularTiemposHastaPelota(pelota.getPrediccionPelota(), this, 0);
        }
        
        /**
         * Si puedo controlar la pelota, calculo el pase que debería hacer
         */
        protected Jugada calcularMejorJugada(PrediccionJugador jugador, UnEquipo equipoContrario) {
            
            Jugada jugada = calcularMejorGol(jugador, equipoContrario);
            if (jugada.getProbabilidad() < PROB_MEDIA) {
                jugada = calcularMejorPase(jugador, equipoContrario);
            }
            
            return jugada;
        }
        
        /**
         * Calcula el mejora pase posible
         *
         * @return Mejor juagada de pase
         */
        protected Jugada calcularMejorPase(PrediccionJugador jugador, UnEquipo equipoContrario) {
            
            double anguloGol = General.getAngulo(jugador.getPosicionJugador().getX(),jugador.getPosicionJugador().getY(),LIMITE_X_MAX, 0);
            Jugada jugadaBuena = null;
            boolean encontradaMejor = false;
            
            // Caché para calculo de predicciones
            PrediccionesYProbabilidad cachePrediccionesProbabilidad[] = new PrediccionesYProbabilidad[ANGULOS_TIRO.length];
            for (int grupo=0; (grupo<PRIORIDADES_TIRO.length) && !encontradaMejor; grupo++) {
                int heuristico = 0;
                /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                //System.out.println("---------------------------------------------- Grupo: "+grupo + "-----------------JUEGA: "+jugador.getNumeroDeJugador());
                /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                for (int i=0; i<PRIORIDADES_TIRO[grupo].length; i++) {
                    int numeroTiro = PRIORIDADES_TIRO[grupo][i];
                    PrediccionesYProbabilidad prediccionesYProbabilidad = calculaPrediccionesYProbabilidadDeJugada(jugador, equipoContrario, numeroTiro, anguloGol, cachePrediccionesProbabilidad);
                    
                    // calculo las coordenadas a las que dirigir el tiro (calculo sin corregir el ángulo)
                    double anguloTiro = getAnguloTiro(numeroTiro, anguloGol);
                    PrediccionPelota[] prediccionPelota = calcularTrazadoHastaCoordenadas(jugador, anguloTiro, false, getFactorFuerza(numeroTiro));
                    
                    double x = prediccionPelota[prediccionPelota.length-1].getX();
                    double y = prediccionPelota[prediccionPelota.length-1].getY();
                    
                    /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                    //double nDis = General.getDistancia(jugador.getPosicionJugador().getX(), jugador.getPosicionJugador().getY(), x, y);
                    //if (nDis < 50) {
                    //    System.out.println("DIS:"+ nDis+" Jugador:"+jugador.getNumeroDeJugador()+" ");
                    //}
                    /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                    
                    Jugada jugada = new Jugada(x,y,prediccionesYProbabilidad.probabilidadDeInterceptar);
                    if (    (jugadaBuena == null) ||
                            (jugada.getProbabilidad() > jugadaBuena.getProbabilidad()) ||
                            ((jugada.getProbabilidad() == jugadaBuena.getProbabilidad()) &&
                            (heuristico < prediccionesYProbabilidad.heuristico))) {
                        
                        /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                        //System.out.println("  >>>   Grupo: "+grupo+" Coefactual: ("+ prediccionesYProbabilidad.heuristico + ">" + heuristico + ") Prob: "+ jugada.getProbabilidad() + " angulo:"+ numeroTiro + " FactorFuerza:" + getFactorFuerza(numeroTiro) +" ¿=? "+ ANGULOS_TIRO[numeroTiro].factorFuerza + " Pto:("+x+","+y+")");
                        /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                        jugadaBuena = jugada;
                        heuristico = prediccionesYProbabilidad.heuristico;
                    } else {
                        /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                        //System.out.println("        Grupo: "+grupo+" Coefactual: "+ prediccionesYProbabilidad.heuristico + " Prob: "+ jugadaBuena.getProbabilidad() + " angulo:"+ numeroTiro + " FactorFuerza:" + getFactorFuerza(numeroTiro) +" ¿=? "+ ANGULOS_TIRO[numeroTiro].factorFuerza + " Pto:("+x+","+y+")");
                        /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                    }
                    encontradaMejor = encontradaMejor || (jugadaBuena.getProbabilidad() >= UMBRAL_PROBABILIDAD[grupo]);
                    
                }
                /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
                //System.out.println("---------------------------------------------- Grupo: "+grupo);
                /////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!111
            }
            //System.out.println("PROB DE RECUPERAR: " + jugadaBuena.getProbabilidad());
            return jugadaBuena;
        }
        
        
        /**
         * Calcula predicciones y probabilidades.
         *
         * @return Predicciones, probabilidades y un heurístico para comprobar como de buena es la jugada.
         */
        private PrediccionesYProbabilidad calculaPrediccionesYProbabilidadDeJugada(PrediccionJugador jugador, UnEquipo equipoContrario, int numeroPase, double anguloGol, PrediccionesYProbabilidad cachePrediccionesProbabilidad[]) {
            int numeroPaseAnt = (ANGULOS_TIRO.length + numeroPase - 2) % ANGULOS_TIRO.length;
            int numeroPasePos = (numeroPase + 2) % ANGULOS_TIRO.length;
            
            PrediccionesYProbabilidad  prediccionesYProbabilidad = calcularCachePrediccionesProbabilidad(numeroPase, jugador, equipoContrario, anguloGol, cachePrediccionesProbabilidad);
            
            PrediccionesYProbabilidad  prediccionesYProbabilidadAnt = calcularCachePrediccionesProbabilidad(numeroPaseAnt, jugador, equipoContrario, anguloGol, cachePrediccionesProbabilidad);
            
            PrediccionesYProbabilidad  prediccionesYProbabilidadDes = calcularCachePrediccionesProbabilidad(numeroPasePos, jugador, equipoContrario, anguloGol, cachePrediccionesProbabilidad);
            
            int numeroPredicciones = prediccionesYProbabilidad.predicciones.size();
            
            //System.out.println("*****************************");
            boolean encontradoJugadorQueLlega = false;
            Point puntoIntercepcionPelota = null;
            
            double distanciaJugadorQueTiraAPorteria = General.getDistancia(jugador.getPosicionJugador().getX(), jugador.getPosicionJugador().getY(), LIMITE_X_MAX, 0);
            
            double heuristicoA = 0; // Recuperar tiro (sin error)
            double heuristicoB = 0; // Recuperar tiro (error Izq)
            double heuristicoC = 0; // Recuperar tiro (error Der)
            double heuristicoD = 0; // Pase con alta probababilidad de Gol
            double heuristicoE = 0; // Pase hacia adelante
            double heuristicoF = 0; // Pase con alto probababilidad de Gol en contra
            for (int i=1; (i<numeroPredicciones) && !encontradoJugadorQueLlega; i++) {
                PrediccionJugador prediccion = prediccionesYProbabilidad.predicciones.get(i);
                if (prediccion.getProbabilidadLlegarAPelota() >= PROB_BAJA) { // && (prediccion.getNumeroDeJugador() != jugador.getNumeroDeJugador())) {
                    encontradoJugadorQueLlega = true;
                    puntoIntercepcionPelota = prediccion.getPuntoIntercepcionPelota();
                    double fuerzaJugador = this.getFuerza(prediccion.getNumeroDeJugador());
                    double distanciaAPorteria = (puntoIntercepcionPelota != null)?
                        General.getDistancia(puntoIntercepcionPelota.getX(), puntoIntercepcionPelota.getY(), LIMITE_X_MAX, 0):
                        1000;
                    
                    // Heurístico si posible gol
                    double fuerzaTiro = fuerzaJugador * FACTOR_FUERZA_PELOTA;
                    double distanciaTiroJugadorAGolSinParada = (fuerzaTiro*fuerzaTiro + fuerzaTiro)/2 - DISTANCIA_RECORRIDO_CONTROLABLE;
                    if (distanciaTiroJugadorAGolSinParada > distanciaAPorteria)
                        heuristicoD = 1000;
                   
                    if (prediccionesYProbabilidad.hayPeligroDeGol)
                        heuristicoF = -1000;
                            
                    // Heuristico avance pelota
                    double avance = distanciaJugadorQueTiraAPorteria - distanciaAPorteria;
                    // El H toma más importancia en mi campo ¡¡¡ mejora el juego un... 0,0000005% !!! (más o menos)
                    double peso = (jugador.getPosicionJugador().getX() < 0)?20000:500; 
                    heuristicoE = ((peso/231)*avance) + peso;
                    //heuristicoE = ((500/231)*avance) + 500;
                    
                }
            }
            
            //prediccionesYProbabilidad.heuristico = (1000 *
            //                (prediccionesYProbabilidadAnt.probabilidadDeInterceptar +
            //                prediccionesYProbabilidadDes.probabilidadDeInterceptar) + heuristico);
            heuristicoA = (1000 / 3) * calcularFactorProbabilidad(prediccionesYProbabilidad.probabilidadDeInterceptar);
            heuristicoB = (250 / 3) * calcularFactorProbabilidad(prediccionesYProbabilidadAnt.probabilidadDeInterceptar);
            heuristicoC = (250 / 3) * calcularFactorProbabilidad(prediccionesYProbabilidadDes.probabilidadDeInterceptar);
            // System.out.print("(" +  heuristicoA + "-" + heuristicoB + "-" + heuristicoC + "-" + heuristicoD + "-" + heuristicoE + "-" + heuristicoF + ")");
            
            prediccionesYProbabilidad.heuristico = (int) Math.round((heuristicoA + heuristicoB + heuristicoC) * (heuristicoD + heuristicoE + heuristicoF)); 
            // heuristicoF -> otro heurístico cojonudo, que en lugar de mejorar el juego... me lo empeora. 
            // Salvo que             // incremente el peso de E si estoy en mi campo.
            
            return prediccionesYProbabilidad;
        }
        
        /**
         * Calcula un factor de probabilidad que me he inventado...
         * Por lo que estoy viendo este método posiblemente deje de tener sentido como tal. 
         *
         * Cachis...
         */
        private double calcularFactorProbabilidad(int probabilidad) {
            /*
            if (probabilidad == PROB_NULA) return 0;
            else if (probabilidad == PROB_BAJA) return 1;
            else if (probabilidad == PROB_MEDIA) return 1;
            return 1;
             */
            if (probabilidad == PROB_NULA) return 0;
            else if (probabilidad == PROB_BAJA) return 0;
            else if (probabilidad == PROB_MEDIA) return 0;
            return 1;
        }
        
        private PrediccionesYProbabilidad  calcularCachePrediccionesProbabilidad(int numeroTiro, PrediccionJugador jugador, UnEquipo equipoContrario, double anguloGol, PrediccionesYProbabilidad cachePrediccionesProbabilidad[]) {
            if (cachePrediccionesProbabilidad[numeroTiro] == null) {
                double anguloTiro = getAnguloTiro(numeroTiro, anguloGol);
                PrediccionPelota[] prediccionPelota = calcularTrazadoHastaCoordenadas(jugador, anguloTiro, true, getFactorFuerza(numeroTiro));
                List<PrediccionJugador> prediccionesAlcancePelotaMisJugadores = calcularTiemposHastaPelota(prediccionPelota, this, 1);
                List<PrediccionJugador> prediccionesAlcancePelotaOtrosJugadores = calcularTiemposHastaPelota(prediccionPelota, equipoContrario, 1);
                cachePrediccionesProbabilidad[numeroTiro] = getPrediccionesYProbabilidadIntercepcion(prediccionesAlcancePelotaMisJugadores, prediccionesAlcancePelotaOtrosJugadores);
            }
            return cachePrediccionesProbabilidad[numeroTiro];
        }
        
        
        private double getAnguloTiro(int numeroTiro, double anguloGol) {
            return anguloGol+ANGULOS_TIRO[numeroTiro].angulo;
        }
        
        private int getFactorFuerza(int numeroTiro) {
            return ANGULOS_TIRO[numeroTiro].factorFuerza;
        }
        
        /**
         * Calcula si hay posibilidad de Gol y hacia donde tirar
         *
         * @return Probabilidad de Gol
         */
        protected Jugada calcularMejorGol(PrediccionJugador jugadorQueControla, UnEquipo equipoContrario) {
            List<Jugada> jugadasDeGol = new ArrayList(PUNTOS_DE_GOL.length + 1);
            Jugada jugada;
            
            // calculo las probabilidades de gol de todos los posibles tiros a puerta
            for (Point puntoGol: PUNTOS_DE_GOL) {
                jugada = calculaJugadaGol(jugadorQueControla, puntoGol, equipoContrario);
                if (jugada != null)
                    jugadasDeGol.add(jugada);
            }
            
            // Pruebo el tiro directo
            jugada = calculaJugadaGol(jugadorQueControla, new Point(LIMITE_X_MAX, (int) jugadorQueControla.getPosicionJugador().getY()), equipoContrario);
            if (jugada != null)
                jugadasDeGol.add(jugada);
            
            // miro cual va ser la mejor de las jugadas calculadas
            Jugada mejorJugada = null;
            for (int i=0; i<jugadasDeGol.size(); i++) {
                jugada = jugadasDeGol.get(i);
                //System.out.print(" - "+jugada.getProbabilidad());
                if (mejorJugada == null) {
                    mejorJugada = jugada;
                } else if (jugada.getProbabilidad() > mejorJugada.getProbabilidad()) {
                    mejorJugada = jugada;
                }
            }
            //System.out.println();
            return mejorJugada;
        }
        
        /**
         * Devuleve la jugada si puede ser Gol. Si no, devolverá "null"
         */
        private Jugada calculaJugadaGol(PrediccionJugador jugador, Point puntoGol, UnEquipo equipoContrario) {
            PrediccionPelota[] trazadoPelota = calcularTrazadoHastaCoordenadas(jugador, (int) puntoGol.getX(), (int) puntoGol.getY(), false, FUERZA_MAXIMA);
            boolean seriaGol = comprobarSiEsGol(trazadoPelota);
            int probabilidad = PROB_NULA;
            double x = trazadoPelota[trazadoPelota.length-1].getX();
            double y = trazadoPelota[trazadoPelota.length-1].getY();
            if (seriaGol) {
                probabilidad = Math.abs(calcularProbabilidadCaptura(trazadoPelota, equipoContrario) - PROB_ALTA);
            }
            return new Jugada(x,y,probabilidad);
        }
        
        protected boolean comprobarSiEsGol(PrediccionPelota[] trazadoPelota) {
            boolean esGol = false;
            for (int i=0; (i<trazadoPelota.length) && !esGol; i++) {
                esGol = trazadoPelota[i].esGolAFavor();
            }
            return esGol;
        }
        
        /**
         * Calcula la probabiliidad de captura de la pelota antes de que sea gol por el equipo contrario.
         *
         * @param trazadoPelota trazado
         * @param equipo Equipo contrario
         * @return Probabilidad de captura de la pelota por el equipo pasado como parámetro
         */
        protected int calcularProbabilidadCaptura(PrediccionPelota[] trazadoPelota, UnEquipo equipoContrario) {
            List<PrediccionJugador> jugadoresQueLlegan = calcularTiemposHastaPelota(trazadoPelota, equipoContrario, 0);
            int size = jugadoresQueLlegan.size();
            int probabilidad = PROB_NULA;
            for (int i=0; i<size; i++) {
                PrediccionJugador jugador = jugadoresQueLlegan.get(i);
                int probabilidadJugador = jugador.getProbabilidadLlegarAPelota();
                Point punto = jugador.getPuntoIntercepcionPelota();
                if ((probabilidadJugador > probabilidad) && !esGolAFavor(punto) && !esGolEnContra(punto))
                    probabilidad = probabilidadJugador;
            }
            return probabilidad;
        }
        
        /**
         * Calcula los punto hasta las coordenadas especificadas
         *
         * @param aplicarLimites Aplica los limites del campo para calcular el trazado
         */
        protected PrediccionPelota[] calcularTrazadoHastaCoordenadas(PrediccionJugador jugadorQueControla, double angulo, boolean aplicarLimites, int factorFuerza) {
            Point pelota = jugadorQueControla.getPuntoIntercepcionPelota();
            double fuerza = getFuerza(jugadorQueControla.getNumeroDeJugador());
            double velocidadReducida = fuerza * factorFuerza * 2 * FACTOR_FUERZA_PELOTA / 100;
            double velocidadLarga = fuerza * FACTOR_FUERZA_PELOTA;
            
            double velocidad = ((factorFuerza < 50) && (velocidadReducida > VELOCIDAD_MINIMA))?velocidadReducida:velocidadLarga;
            
            //if ((factorFuerza < 50) && (velocidadReducida > VELOCIDAD_MINIMA)) {
            //    System.out.println("Velocidad R -> " + velocidadReducida+ " ; L " + velocidadLarga+ " T: " + velocidad + " F:" + fuerza + " FF:"+factorFuerza);
            //}
            
            PrediccionPelota[] trazadoPelota = new PrediccionPelota[NUM_PREDICCIONES];
            double xSig = pelota.getX();
            double ySig = pelota.getY();
            //vector.add(new Point(xSig, ySig));
            trazadoPelota[0] = new PrediccionPelota(xSig, ySig, velocidad);
            for (int i=0; i<NUM_PREDICCIONES; i++) {
                if (velocidad >= 0) {
                    xSig +=  Math.cos(angulo) * velocidad;
                    ySig +=  Math.sin(angulo) * velocidad;
                    if (aplicarLimites) {
                        Trayectoria trayectoria = corregirTrayectoria(xSig, ySig, angulo);
                        xSig = trayectoria.x;
                        ySig = trayectoria.y;
                        angulo = trayectoria.angulo;
                    }
                    if (aplicarLimites && (esGolAFavor(xSig, ySig) || esGolEnContra(xSig, ySig))) {
                        velocidad = 0;
                    } else {
                        velocidad--;
                    }
                }
                if (aplicarLimites)
                    trazadoPelota[i] = new PrediccionPelota(limitarX(xSig), limitarY(ySig), velocidad);
                else
                    trazadoPelota[i] = new PrediccionPelota(xSig, ySig, velocidad);
            }
            return trazadoPelota;
        }
        
        /**
         * Calcula los punto hasta las coordenadas especificadas
         *
         * @param aplicarLimites Aplica los limites del campo para calcular el trazado
         */
        protected PrediccionPelota[] calcularTrazadoHastaCoordenadas(PrediccionJugador jugadorQueControla, int xDes, int yDes, boolean aplicarLimites, int factorFuerza) {
            Point pelota = jugadorQueControla.getPuntoIntercepcionPelota();
            double angulo = General.getAngulo(pelota.getX(),pelota.getY(), xDes, yDes);
            return calcularTrazadoHastaCoordenadas(jugadorQueControla, angulo, aplicarLimites, factorFuerza);
        }
        
        protected void calcularPase(PrediccionJugador jugadorQueControla, UnEquipo equipoContrario) {
            Point pelota = jugadorQueControla.getPuntoIntercepcionPelota();
        }
        
        
        /**
         * Añade la predicción del jugador de forma ordenada en la lista de predicciones.
         * Se ordena por tiempo en llegar a la pelota.
         *
         * @param prediccionJugadorOrdenados Lista
         * @param prediccionJugador Predicción
         **/
        protected void anadirPrediccionOrdenada(List<PrediccionJugador> prediccionJugadorOrdenados, PrediccionJugador prediccionJugador) {
            int size = prediccionJugadorOrdenados.size();
            boolean introducidoEnLista = false;
            for (int i=0; (i<size) && !introducidoEnLista; i++) {
                PrediccionJugador prediccionJugadorLista = prediccionJugadorOrdenados.get(i);
                if (prediccionJugadorLista != null) {
                    if (prediccionJugador.getTiempoHastaPelota() < prediccionJugadorLista.getTiempoHastaPelota()) {
                        prediccionJugadorOrdenados.add(i, prediccionJugador);
                        introducidoEnLista = true;
                    }
                }
            }
            if (!introducidoEnLista)
                prediccionJugadorOrdenados.add(prediccionJugador);
        }
        
        
        /**
         * Reordena las predicciones en función del equipo contrario.
         *
         * @param prediccionesMias Lista
         * @param prediccionesContrario  Lista
         **/
        protected void calcularPrediccionesYProbabilidadIntercepcion(List<PrediccionJugador> prediccionesMias, List<PrediccionJugador> prediccionesContrario) {
            PrediccionesYProbabilidad prediccionesYProbabilidad = getPrediccionesYProbabilidadIntercepcion(prediccionesMias, prediccionesContrario);
            setPrediccionesTacticas(prediccionesYProbabilidad.predicciones);
            setProbabilidadDeInterceptarPelota(prediccionesYProbabilidad.probabilidadDeInterceptar);
        }
        
        
        /**
         * Reordena las predicciones en función del equipo contrario.
         *
         * @param prediccionesMias Lista
         * @param prediccionesContrario  Lista
         * @param aPartirDe Tiempo a partir del cual debe calcularse la predicción
         * @return predicciones y probabilidades
         **/
        protected PrediccionesYProbabilidad getPrediccionesYProbabilidadIntercepcion(List<PrediccionJugador> prediccionesMias, List<PrediccionJugador> prediccionesContrario) {
            /*
            System.out.println("********************************* MALOS ***********************************");
            for (PrediccionJugador prediccionOtro: prediccionesContrario) {
                System.out.println("jugador: "+prediccionOtro.getNumeroDeJugador() + " - tpo a pelota -> " + Math.round(prediccionOtro.getTiempoHastaPelota())+" PROB:"+prediccionOtro.getProbabilidadLlegarAPelota());
            }
            System.out.println("--------------------------------- BUENOS ----------------------------------");
            for (PrediccionJugador prediccionOtro: prediccionesMias) {
                System.out.println("jugador: "+prediccionOtro.getNumeroDeJugador() + " - tpo a pelota -> " + Math.round(prediccionOtro.getTiempoHastaPelota())+" PROB:"+prediccionOtro.getProbabilidadLlegarAPelota());
            }
            */
            
            int probDeLlevarseLaPelota = PROB_NULA;
            boolean peligroDeQueMeMetanGol = false;

            if (prediccionesMias.size() > 0) {
                PrediccionJugador prediccionMia = prediccionesMias.get(0);
                
                if (prediccionMia.getProbabilidadLlegarAPelota() >= PROB_ALTA) {
                    double tiempoAlcancePelotaMiJugador = prediccionMia.getTiempoHastaPelota();
                    double probAbsolutaDeLlevarseLaPelota = 1; // Por ahora, le doy la máxima probabilidad
                    int numeroJugadorMio = prediccionMia.getNumeroDeJugador();
                    double velocidadJugadorMio = prediccionMia.getVelocidadJugador();
                    for (PrediccionJugador prediccionJugadorContrario: prediccionesContrario) {

                        if (prediccionJugadorContrario.getProbabilidadLlegarAPelota() >= PROB_ALTA) {
                            //System.out.println("jugador: "+prediccionJugadorContrario.getNumeroDeJugador() + " - tpo a pelota -> " + Math.round(prediccionJugadorContrario.getTiempoHastaPelota())+" PROB:"+prediccionJugadorContrario.getProbabilidadLlegarAPelota());
                            int numeroJugadorContrario = prediccionJugadorContrario.getNumeroDeJugador();
                            double fuerzaJugadorContrario = prediccionJugadorContrario.getFuerzaJugador();
                            double velocidadJugadorContrario = prediccionJugadorContrario.getVelocidadJugador();
                            double fuerzaTiroJugadorContrario = fuerzaJugadorContrario * FACTOR_FUERZA_PELOTA;
                            double distanciaPeligroAPorteria = (fuerzaTiroJugadorContrario*fuerzaTiroJugadorContrario + fuerzaTiroJugadorContrario)/2 - DISTANCIA_RECORRIDO_CONTROLABLE;
                            Point puntoIntercepcionPelota = prediccionJugadorContrario.getPuntoIntercepcionPelota();
                            double distanciaJugadorContrarioPorteria = General.getDistancia(puntoIntercepcionPelota.getX(), puntoIntercepcionPelota.getY(), LIMITE_X_MIN, 0);
                         
                            double diferenciaDeTiempo = prediccionMia.getTiempoHastaPelota() - prediccionJugadorContrario.getTiempoHastaPelota();
                            
                            if (diferenciaDeTiempo < 0) { // Llega primero mi jugador
                                // Calculo a que distancia de la pelota estará el jugador contrario para saber si puede golpearla
                                double distanciaALaPelota = Math.abs(velocidadJugadorContrario * diferenciaDeTiempo);
                                if (distanciaALaPelota <= DISTANCIA_ALCANCE_BOLA) { // Quiere decir que tiene las mismas posibilidades de llegar
                                    probAbsolutaDeLlevarseLaPelota *= 0.5;
                                    peligroDeQueMeMetanGol |= (distanciaJugadorContrarioPorteria <= distanciaPeligroAPorteria);
                                }
                            } else { // Llega primero el jugador contrario
                                // Calculo a que distancia de la pelota estará mi jugador para saber si puede golpearla
                                double distanciaALaPelota = Math.abs(velocidadJugadorMio * diferenciaDeTiempo);
                                if (distanciaALaPelota <= DISTANCIA_ALCANCE_BOLA) { // Quiere decir que tiene las mismas posibilidades de llegar
                                    probAbsolutaDeLlevarseLaPelota *= 0.5;
                                    peligroDeQueMeMetanGol |= (distanciaJugadorContrarioPorteria <= distanciaPeligroAPorteria);
                                } else {
                                    probAbsolutaDeLlevarseLaPelota *= 0;
                                }
                            }
                        }
                    }
                      
                    probDeLlevarseLaPelota = (int) Math.round(3*probAbsolutaDeLlevarseLaPelota);
                    
                    if ((probDeLlevarseLaPelota > 0) && (probDeLlevarseLaPelota == PROB_NULA)) probDeLlevarseLaPelota = PROB_BAJA;
                } 
            }
            
            //System.out.println("# PROBABILIDAD REAL DE INTERCEPTAR ----> " + probDeLlevarseLaPelota);

            //return new PrediccionesYProbabilidad(prediccionesMias, probDeLlevarseLaPelota);

            ////////////////////////////////////////////// EL CÓDIGO SIGUIENTE CREO QUE NO HACE FALTA.... joeeeeee que lío
        
            int size = prediccionesContrario.size();
            PrediccionJugador alcancePelotaContrario = null;

            for (int i=0; (i<size) && (alcancePelotaContrario == null); i++) {
                PrediccionJugador prediccionContrario = prediccionesContrario.get(i);
                alcancePelotaContrario = (prediccionContrario.getProbabilidadLlegarAPelota() >= PROB_MEDIA)?
                    prediccionContrario:
                    null;
            }
            
            List<PrediccionJugador> prediccionesAux = new ArrayList(NUM_JUGADORES);
            prediccionJugadorOrdenados = new ArrayList(NUM_JUGADORES);
            int probabilidadDeInterceptar = PROB_NULA;
            boolean jugadorConTactica[] = {false,false,false,false,false,false,false,false,false,false,false};
            for (PrediccionJugador prediccionMia: prediccionesMias) {
                int numeroJugador = prediccionMia.getNumeroDeJugador();
                double tiempoHastaPelota = prediccionMia.getTiempoHastaPelota();

                if ((alcancePelotaContrario == null) || (tiempoHastaPelota < alcancePelotaContrario.getTiempoHastaPelota())) {
                    int probAlcance = prediccionMia.getProbabilidadLlegarAPelota();
                    
                    // Si la probabilidad es alta
                    if (probAlcance >= PROB_ALTA) {
                        // Borro posible orden anterior
                        for (PrediccionJugador prediccionAnterior: prediccionesAux) {
                            if ((prediccionAnterior.getNumeroDeJugador() == numeroJugador)
                                && prediccionAnterior.getProbabilidadLlegarAPelota() < probAlcance) {
                                prediccionesAux.remove(prediccionAnterior); // elimino las predicciones anteriores
                                jugadorConTactica[numeroJugador] = false;
                                break;
                            }
                        }
                    }
                    if (probAlcance > probabilidadDeInterceptar)
                        probabilidadDeInterceptar = probAlcance;
                }
                // Añado la tactica siempre que no la tenga ya en la lista
                if (!jugadorConTactica[numeroJugador]) {
                    prediccionesAux.add(prediccionMia);
                    jugadorConTactica[numeroJugador] = true;
                }
            }
            
            
            //System.out.println("Probs -> "+ probabilidadDeInterceptar + " - " + probDeLlevarseLaPelota + " : " + peligroDeQueMeMetanGol);
            return new PrediccionesYProbabilidad(prediccionesAux, probabilidadDeInterceptar, peligroDeQueMeMetanGol);
            
            // PUES ESO; que al final me sobra código... muchas predicciones y solo consigo bajar el nivel de juego
            //return new PrediccionesYProbabilidad(prediccionesMias, probDeLlevarseLaPelota, peligroDeQueMeMetanGol);
            
        
        }
        
        private class PrediccionesYProbabilidad {
            public PrediccionesYProbabilidad(List<PrediccionJugador> predicciones, int probabilidadDeInterceptar, int heuristico) {
                this.predicciones = predicciones;
                this.probabilidadDeInterceptar = probabilidadDeInterceptar;
                this.heuristico = heuristico;
            }
            public PrediccionesYProbabilidad(List<PrediccionJugador> predicciones, int probabilidadDeInterceptar, boolean hayPeligroDeGol) {
                this.predicciones = predicciones;
                this.probabilidadDeInterceptar = probabilidadDeInterceptar;
                this.heuristico = heuristico;
                this.hayPeligroDeGol = hayPeligroDeGol;
            }
            public List<PrediccionJugador> predicciones;
            public int probabilidadDeInterceptar;
            public int heuristico;
            public boolean hayPeligroDeGol = false;
        }
        
        public List<PrediccionJugador> getJugadoresMasAccesiblesAPelota() {
            return prediccionJugadorOrdenados;
        }
        
        protected List<PrediccionJugador> getPrediccionesTacticas() {
            return prediccionesTacticas;
        }
        
        public int getProbabilidadDeInterceptarPelota() {
            return probabilidadDeInterceptarPelota;
        }
        
        protected void setPrediccionesTacticas(List<PrediccionJugador> prediccionesTacticas) {
            this.prediccionesTacticas = prediccionesTacticas;
        }
        
        protected void setProbabilidadDeInterceptarPelota(int prob) {
            probabilidadDeInterceptarPelota = prob;
        }
    }
    
    /**
     * Predicción de la pelota
     */
    private class PrediccionPelota {
        private Point puntoPelota = new Point(0, 0);
        private double velocidad = 0;
        
        public PrediccionPelota() {
        }
        
        public PrediccionPelota(double x, double y, double velocidad) {
            setCoordenadas(new Point((int) x,(int) y));
            setVelocidad(velocidad);
        }
        
        public double getVelocidad() {
            return velocidad;
        }
        
        public Point getCoordenadas() {
            return puntoPelota;
        }
        
        public void setVelocidad(double velocidad) {
            this.velocidad = velocidad;
        }
        
        public void setCoordenadas(Point puntoPelota) {
            this.puntoPelota = puntoPelota;
        }
        
        public double getX() {
            return puntoPelota.getX();
        }
        
        public double getY() {
            return puntoPelota.getY();
        }
        
        public int getProbabilidadDePararPelota() {
            if (getVelocidad() < (VEL_PELOTA_LIMITE_CONTROL-MARGEN_DUDA_VELOCIDAD))
                return PROB_ALTA;
            if (getVelocidad() < (VEL_PELOTA_LIMITE_CONTROL))
                return PROB_MEDIA;
            return PROB_NULA;
        }
        
        public boolean esGolAFavor() {
            return TacticaPhilTheBest.esGolAFavor(puntoPelota);
        }
        
        public boolean esGolEnContra() {
            return TacticaPhilTheBest.esGolEnContra(puntoPelota);
        }
    }
    
    private class Jugada {
        private int probabilidad;
        private double x;
        private double y;
        
        public Jugada(double x, double y, int probabilidad) {
            this.x = x;
            this.y = y;
            this.probabilidad = probabilidad;
        }
        
        public double getX() {
            return x;
        }
        public double getY() {
            return y;
        }
        public int getProbabilidad() {
            return probabilidad;
        }
        public void setX(double x) {
            this.x = x;
        }
        public void setY(double y) {
            this.y = y;
        }
        public void setProbabilidad(int probabilidad) {
            this.probabilidad = probabilidad;
        }
        
    }
    
    /**
     * Mi equipo
     */
    private class MiEquipo extends UnEquipo {
        /*
        private double estadoInicial[][]={
            {-150,0,4},  // Jugador 1[0]
            {-120,-30,4},  // Jugador 2[1]
            {-100,30,4},  // Jugador 3[2]
            {-100,0,4},  // Jugador 4[3]
            {-80,50,4},  // Jugador 5[4]
            {-60,0,4},  // Jugador 6[5]
            {-20,-100,4},  // Jugador 7[6]
            {0,0,4},  // Jugador 8[7]
            {50,100,4},  // Jugador 9[8]
            {100,50,4},  // Jugador 10[9]
            {120,-50,4}  // Jugador 11[10]
        };
         */
        
        private double estadoInicial[][]={
            //{-150,0,6, PORTERO},  // Jugador 1[0]
            {-150,0,7, PORTERO},  // Jugador 1[0]
            {-120,-30,5, DEFENSA},  // Jugador 2[1]
            {-100,30,5, DEFENSA},  // Jugador 3[2]
            {-100,0,6, DEFENSA},  // Jugador 4[3]
            {-80,50,6, DEFENSA},  // Jugador 5[4]
            {-60,0,6, MEDIO},  // Jugador 6[5]
            {-20,-100,6, MEDIO},  // Jugador 7[6]
            {0,0,4, LIBRE},  // Jugador 8[7]
            {50,100,6, DELANTERO},  // Jugador 9[8]
            {100,50,6, DELANTERO},  // Jugador 10[9]
            {120,-50,7, DELANTERO}  // Jugador 11[10]
        };
        
        /** Prediccion de colocación de los jugadores */
        private List<PrediccionJugador> prediccionColocacionJugadores;
        
        /** Lista con todas las estrategias */
        private Vector<Comando> estrategia;
        
        /** Matriz para ver almacenar qué jugadores tienen ya una orden de juego */
        boolean jugadoresConOrdenes[] = new boolean[NUM_JUGADORES];
        
        
        public MiEquipo() {
            super();
        }
        
        /**
         * Actualiza el estado del equipo
         */
        public void actualizarEquipo(Partido partido, Point[] jugadores) {
            resetJugadoresConOrden();
            super.actualizarEquipo(partido, jugadores);
            //System.out.println("--------------------------------------------------");
            estrategia = new Vector();
            estrategia = calcularPosicionamientoInicial(partido, estrategia);
            calcularPrediccionesYProbabilidadIntercepcion(getJugadoresMasAccesiblesAPelota(),
                    partido.getOtroEquipo().getJugadoresMasAccesiblesAPelota());
            estrategia = calcularEstrategiaAtaque(partido, estrategia);
            calculaColocacionJugadores();
            estrategia = calcularEstrategiaColocacion(partido, estrategia);
            
        }
        
        private boolean tieneOrdenesElJugador(int numJugador) {
            return jugadoresConOrdenes[numJugador];
        }
        
        private void setJugadorConOrden(int numJugador) {
            jugadoresConOrdenes[numJugador] = true;
        }
        
        private void resetJugadoresConOrden() {
            //for (boolean jugadorConOrden: jugadoresConOrdenes)
            for (int i=0; i<jugadoresConOrdenes.length; i++)
                jugadoresConOrdenes[i] = false;
        }
        private boolean[] getJugadoresConOrdenes() {
            return jugadoresConOrdenes;
        }
        /**
         * Devuelve la estrategia a adoptar
         */
        private Vector<Comando> getEstrategia() {
            return this.estrategia;
        }
        
        
        /**
         * Calcula el posicionamiento inicial de los jugadores
         */
        private Vector<Comando> calcularPosicionamientoInicial(Partido partido, Vector<Comando> com)  {
            MiEquipo miEquipo = partido.getMiEquipo();
            for (int n=0;n<NUM_JUGADORES;n++)
                com.add(new ComandoIrA(n,miEquipo.getXInicial(n),miEquipo.getYInicial(n)));
            return com;
        }
        
        /**
         * Calcula la colocación de jugadores en el ataque
         */
        private Vector<Comando> calcularEstrategiaColocacion(Partido partido, Vector<Comando> com)  {
            List<PrediccionJugador> jugadoresAMover = this.getPrediccionesColocacionJugadores();
            for (PrediccionJugador jugadorAMovilizar: jugadoresAMover) {
                com.add(new ComandoIrA(jugadorAMovilizar.getNumeroDeJugador(), jugadorAMovilizar.getPuntoIntercepcionPelota().getX(), jugadorAMovilizar.getPuntoIntercepcionPelota().getY()));
                //System.out.println("A SEGUNDAS: "+jugadorAMovilizar.getNumeroDeJugador());
            }
            
            return com;
        }
        
        
        /**
         * Calcula la estrategia de ataque y de envío a Gol
         */
        private Vector<Comando> calcularEstrategiaAtaque(Partido partido, Vector<Comando> com)  {
            
            int jugadoresAPelota = (getProbabilidadDeInterceptarPelota() <= PROB_MEDIA)?2:1;
            
            Pelota pelota = partido.getPelota();
            
            List<PrediccionJugador> jugadoresMasAccesiblesAPelota = this.getPrediccionesTacticas();
            boolean segurosDeLlegar = false;
            int numeroJugadoresQueLleganSeguro = 0;
            for (int i=0; (i<jugadoresMasAccesiblesAPelota.size()) && !segurosDeLlegar; i++) {
                PrediccionJugador jugadorMasAccesibleAPelota = jugadoresMasAccesiblesAPelota.get(i);
                int numeroJugador = jugadorMasAccesibleAPelota.getNumeroDeJugador();
                int xDestino = (int) jugadorMasAccesibleAPelota.getPuntoIntercepcionPelota().getX();
                int yDestino = (int) jugadorMasAccesibleAPelota.getPuntoIntercepcionPelota().getY();
                //System.out.println("A PELOTA: "+numeroJugador);
                if (jugadorMasAccesibleAPelota.controlaLaPelota(pelota.getPosicionActual())) {
                    com.add(new ComandoIrA(numeroJugador, pelota.getXActual(), pelota.getYActual()));
                    Jugada jugada = this.calcularMejorJugada(jugadorMasAccesibleAPelota);
                    com.add(new ComandoGolpearBola(numeroJugador, jugada.getX(), jugada.getY()));
                } else {
                    if (jugadorMasAccesibleAPelota.getProbabilidadLlegarAPelota() >= PROB_BAJA)
                        com.add(new ComandoIrA(jugadorMasAccesibleAPelota.getNumeroDeJugador(), xDestino, yDestino));
                }
                //System.out.println(" JUGADOR CON ORDEN -> " + numeroJugador);
                setJugadorConOrden(numeroJugador);
                if (jugadorMasAccesibleAPelota.getProbabilidadLlegarAPelota() >= PROB_ALTA) {
                    //System.out.println("SEGURO DE LLEGAR");
                    numeroJugadoresQueLleganSeguro++;
                    //if (pelota.esAtaque()) {
                        segurosDeLlegar = (numeroJugadoresQueLleganSeguro >= jugadoresAPelota);
                    //} else {
                      //  segurosDeLlegar = (numeroJugadoresQueLleganSeguro >= JUGADORES_VAN_A_PELOTA_DEFENSA);
                    //}
                }
            }
            
            return com;
        }
        
        /**
         * Calcula la mejor jugada que puedo hacer
         */
        public Jugada calcularMejorJugada(PrediccionJugador jugadorMasAccesibleAPelota) {
            return calcularMejorJugada(jugadorMasAccesibleAPelota, partido.getOtroEquipo());
        }
        
        /**
         * Devuelve la predicción de colocación de jugadores
         *
         * @return Colocación de jugadores
         */
        public List<PrediccionJugador> getPrediccionesColocacionJugadores() {
            return prediccionColocacionJugadores;
        }
        
        /**
         * Calcula la colocación de los jugadores
         */
        private void calculaColocacionJugadores() {
            List<PrediccionJugador> prediccionColocacionJugadores = new ArrayList(NUM_JUGADORES);
            prediccionColocacionJugadores = calculaColocacionIntercepcionSiguientePase(prediccionColocacionJugadores);
            prediccionColocacionJugadores = calculaColocacionDelanteros(prediccionColocacionJugadores);
            prediccionColocacionJugadores = calculaColocacionMedios(prediccionColocacionJugadores);
            prediccionColocacionJugadores = calculaColocacionDefensa(prediccionColocacionJugadores);
            this.prediccionColocacionJugadores = prediccionColocacionJugadores;
        }

        
        /**
         * Calcula la posición de los jugadores delanteros.
         * Por cada fuerza, coloca un jugador en el eje con la portería contraria.
         *
         */
        private List<PrediccionJugador> calculaColocacionMedios(List<PrediccionJugador> prediccionColocacionJugadores) {
            Pelota pelota = partido.getPelota();
            double xPelota = pelota.getXActual();
            double yPelota = pelota.getYActual();

            List<Integer>[] listasMediosPorfuerza = new ArrayList[8];

            // Ordeno los DELANTEROS por fuerza
            for (int i=0; i<NUM_JUGADORES; i++) {
                if ((getTipoJugador(i) == MEDIO) && !tieneOrdenesElJugador(i)) {
                    int fuerzaJugador = (int) getFuerza(i);
                    List<Integer> listaJugadores;
                    if (listasMediosPorfuerza[fuerzaJugador] == null) {
                        listaJugadores = new ArrayList();
                        listasMediosPorfuerza[fuerzaJugador] = listaJugadores;
                    } else {
                        listaJugadores = listasMediosPorfuerza[fuerzaJugador];
                    }
                    listaJugadores.add(i);
                }
            }

            // Por cada grupo de fuerza, movilizo un jugador al eje pelota-portería_contraria (al más cercano de ese eje)
            // y a los otros a una distancia optima de la portería
            for (int grupoFuerza=0; grupoFuerza<listasMediosPorfuerza.length; grupoFuerza++) {
                if (listasMediosPorfuerza[grupoFuerza] != null) {
                    double fuerza = (grupoFuerza < 6)?6:grupoFuerza;
                    double fuerzaTiro = fuerza * FACTOR_FUERZA_PELOTA;
                    double fuerzaMaximaTiro = MAXIMA_FUERZA * FACTOR_FUERZA_PELOTA;
                    
                    // La distancia óptima a portería corresponde a la distancia de los jugadores con máxima potencia
                    // más distancia de tiro optimo del jugador
                    
                    double distanciaOptimaDeTiro = (fuerzaTiro*fuerzaTiro + fuerzaTiro)/2 - DISTANCIA_RECORRIDO_CONTROLABLE;
                    double distanciaOptimaDeTiroMaximaPotencia = 
                            (fuerzaMaximaTiro*fuerzaMaximaTiro + fuerzaMaximaTiro)/2 - DISTANCIA_RECORRIDO_CONTROLABLE;
                    double distanciaOptimaAPorteria = distanciaOptimaDeTiroMaximaPotencia + distanciaOptimaDeTiro;

                    int jugadorMasCercanoAPelotaFuerzaI = -1;
                    double distanciaMasCorta = 1000;
                    Iterator<Integer> it = listasMediosPorfuerza[grupoFuerza].iterator();
                    while (it.hasNext()) {
                        int numJugador = it.next();
                        double distanciaPelota = General.getDistancia(
                            getPosicionJugador(numJugador).getX(), getPosicionJugador(numJugador).getX(),
                            xPelota, yPelota);
                        if (distanciaPelota < distanciaMasCorta) {
                            distanciaMasCorta = distanciaPelota;
                            jugadorMasCercanoAPelotaFuerzaI = numJugador;
                        }
                    }
                    
                    List<Double> angulosUtilizados = new ArrayList();
                    
                    // Ya tengo el jugador más cercano -> lo movilizo
                    if (jugadorMasCercanoAPelotaFuerzaI >= 0) {
                        if (distanciaOptimaAPorteria > 0) {
                            double angulo = General.getAngulo(xPelota, yPelota, LIMITE_X_MAX, 0);
                            double x = LIMITE_X_MAX - Math.cos(angulo) * distanciaOptimaAPorteria;
                            double y = 0 - Math.sin(angulo) * distanciaOptimaAPorteria;
                            PrediccionJugador prediccion = new PrediccionJugador(jugadorMasCercanoAPelotaFuerzaI,
                                    getVelocidad(jugadorMasCercanoAPelotaFuerzaI),
                                    getPosicionJugador(jugadorMasCercanoAPelotaFuerzaI).getX(),getPosicionJugador(jugadorMasCercanoAPelotaFuerzaI).getY());
                            prediccion.setPuntoIntercepcionPelota(new Point((int) Math.round(x), (int) Math.round(y)));
                            prediccionColocacionJugadores.add(prediccion);
                            setJugadorConOrden(jugadorMasCercanoAPelotaFuerzaI);
                            angulosUtilizados.add(angulo);
                        }
                    }

                    // Movilizo a los demás jugadores
                    it = listasMediosPorfuerza[grupoFuerza].iterator();
                    while (it.hasNext()) {
                        int numJugador = it.next();
                        if (numJugador != jugadorMasCercanoAPelotaFuerzaI) {
                            if (distanciaOptimaAPorteria > 0) {
                                double angulo = General.getAngulo(getPosicionJugador(numJugador).getX(), getPosicionJugador(numJugador).getY(), LIMITE_X_MAX, 0);
                                //angulo = buscarMejorAngulo(angulo, angulosUtilizados, ANGULO_SEPARACION_DEFENSAS, Math.PI);
                                double x = LIMITE_X_MAX - Math.cos(angulo) * distanciaOptimaAPorteria;
                                double y = 0 - Math.sin(angulo) * distanciaOptimaAPorteria;
                                PrediccionJugador prediccion = new PrediccionJugador(numJugador,
                                        getVelocidad(numJugador),
                                        getPosicionJugador(numJugador).getX(),getPosicionJugador(numJugador).getY());
                                prediccion.setPuntoIntercepcionPelota(new Point((int) Math.round(x), (int) Math.round(y)));
                                prediccionColocacionJugadores.add(prediccion);
                                setJugadorConOrden(numJugador);
                                angulosUtilizados.add(angulo);
                            }
                        }
                    }
                }
            }
            
            return prediccionColocacionJugadores;
        }
        
        
        
        /**
         * Calcula la posición de los jugadores delanteros.
         * Por cada fuerza, coloca un jugador en el eje con la portería contraria.
         *
         */
        private List<PrediccionJugador> calculaColocacionDelanteros(List<PrediccionJugador> prediccionColocacionJugadores) {
            Pelota pelota = partido.getPelota();
            double xPelota = pelota.getXActual();
            double yPelota = pelota.getYActual();

            List<Integer>[] listasDelanterosPorfuerza = new ArrayList[8];

            // Ordeno los DELANTEROS por fuerza
            for (int i=0; i<NUM_JUGADORES; i++) {
                if ((getTipoJugador(i) == DELANTERO) && !tieneOrdenesElJugador(i)) {
                    int fuerzaJugador = (int) getFuerza(i);
                    List<Integer> listaJugadores;
                    if (listasDelanterosPorfuerza[fuerzaJugador] == null) {
                        listaJugadores = new ArrayList();
                        listasDelanterosPorfuerza[fuerzaJugador] = listaJugadores;
                    } else {
                        listaJugadores = listasDelanterosPorfuerza[fuerzaJugador];
                    }
                    listaJugadores.add(i);
                }
            }

            // Por cada grupo de fuerza, movilizo un jugador al eje pelota-portería_contraria (al más cercano de ese eje)
            // y a los otros a una distancia optima de la portería
            for (int grupoFuerza=0; grupoFuerza<listasDelanterosPorfuerza.length; grupoFuerza++) {
                if (listasDelanterosPorfuerza[grupoFuerza] != null) {
                    double fuerza = grupoFuerza;
                    double fuerzaTiro = fuerza * FACTOR_FUERZA_PELOTA;
                    double distanciaOptimaAPorteria = (fuerzaTiro*fuerzaTiro + fuerzaTiro)/2 - DISTANCIA_RECORRIDO_CONTROLABLE;

                    int jugadorMasCercanoAPelotaFuerzaI = -1;
                    double distanciaMasCorta = 1000;
                    Iterator<Integer> it = listasDelanterosPorfuerza[grupoFuerza].iterator();
                    while (it.hasNext()) {
                        int numJugador = it.next();
                        double distanciaPelota = General.getDistancia(
                            getPosicionJugador(numJugador).getX(), getPosicionJugador(numJugador).getX(),
                            xPelota, yPelota);
                        if (distanciaPelota < distanciaMasCorta) {
                            distanciaMasCorta = distanciaPelota;
                            jugadorMasCercanoAPelotaFuerzaI = numJugador;
                        }
                    }
                    
                    List<Double> angulosUtilizados = new ArrayList();
                    
                    // Ya tengo el jugador más cercano -> lo movilizo
                    if (jugadorMasCercanoAPelotaFuerzaI >= 0) {
                        if (distanciaOptimaAPorteria > 0) {
                            double angulo = General.getAngulo(xPelota, yPelota, LIMITE_X_MAX, 0);
                            double x = LIMITE_X_MAX - Math.cos(angulo) * distanciaOptimaAPorteria;
                            double y = 0 - Math.sin(angulo) * distanciaOptimaAPorteria;
                            PrediccionJugador prediccion = new PrediccionJugador(jugadorMasCercanoAPelotaFuerzaI,
                                    getVelocidad(jugadorMasCercanoAPelotaFuerzaI),
                                    getPosicionJugador(jugadorMasCercanoAPelotaFuerzaI).getX(),getPosicionJugador(jugadorMasCercanoAPelotaFuerzaI).getY());
                            prediccion.setPuntoIntercepcionPelota(new Point((int) Math.round(x), (int) Math.round(y)));
                            prediccionColocacionJugadores.add(prediccion);
                            setJugadorConOrden(jugadorMasCercanoAPelotaFuerzaI);
                            angulosUtilizados.add(angulo);
                        }
                    }

                    // Movilizo a los demás jugadores
                    it = listasDelanterosPorfuerza[grupoFuerza].iterator();
                    while (it.hasNext()) {
                        int numJugador = it.next();
                        if (numJugador != jugadorMasCercanoAPelotaFuerzaI) {
                            if (distanciaOptimaAPorteria > 0) {
                                double angulo = General.getAngulo(getPosicionJugador(numJugador).getX(), getPosicionJugador(numJugador).getY(), LIMITE_X_MAX, 0);
                                //angulo = buscarMejorAngulo(angulo, angulosUtilizados, ANGULO_SEPARACION_DEFENSAS, Math.PI);
                                double x = LIMITE_X_MAX - Math.cos(angulo) * distanciaOptimaAPorteria;
                                double y = 0 - Math.sin(angulo) * distanciaOptimaAPorteria;
                                PrediccionJugador prediccion = new PrediccionJugador(numJugador,
                                        getVelocidad(numJugador),
                                        getPosicionJugador(numJugador).getX(),getPosicionJugador(numJugador).getY());
                                prediccion.setPuntoIntercepcionPelota(new Point((int) Math.round(x), (int) Math.round(y)));
                                prediccionColocacionJugadores.add(prediccion);
                                setJugadorConOrden(numJugador);
                                angulosUtilizados.add(angulo);
                            }
                        }
                    }
                }
            }
            
            return prediccionColocacionJugadores;
        }

        /**
         * Calcula la posición del jugadores de defensa
         * Los defensas los pongo en la zona de alto riesgo correspondiente a jugadores con un punto menos de velocidad,
         * (que viene a ser lo mismo que [fuerza+1])
         */
        private List<PrediccionJugador> calculaColocacionDefensa(List<PrediccionJugador> prediccionColocacionJugadores) {
            Pelota pelota = partido.getPelota();
            double xPelota = pelota.getXActual();
            double yPelota = pelota.getYActual();
            
            List<Integer>[] listasDefensasPorfuerza = new ArrayList[8];

            // Ordeno los DEFENSA por fuerza
            for (int i=0; i<NUM_JUGADORES; i++) {
                if ((getTipoJugador(i) == DEFENSA) && !tieneOrdenesElJugador(i)) {
                    int fuerzaJugador = (int) getFuerza(i);
                    List<Integer> listaJugadores;
                    if (listasDefensasPorfuerza[fuerzaJugador] == null) {
                        listaJugadores = new ArrayList();
                        listasDefensasPorfuerza[fuerzaJugador] = listaJugadores;
                    } else {
                        listaJugadores = listasDefensasPorfuerza[fuerzaJugador];
                    }
                    listaJugadores.add(i);
                }
            }
            
            // Por cada grupo de fuerza, movilizo un jugador al eje pelota-mi_portería (al más cercano de ese eje)
            // y a los otros a una distancia optima de la portería
            for (int grupoFuerza=0; grupoFuerza<listasDefensasPorfuerza.length; grupoFuerza++) {
                if (listasDefensasPorfuerza[grupoFuerza] != null) {
                    double fuerzaJugador = grupoFuerza;
                    double fuerzaAtacantes = fuerzaJugador + 1;
                    double fuerzaTiroAtaque = fuerzaAtacantes * FACTOR_FUERZA_PELOTA;
                    double distanciaAltoRiesgoAPorteria = (fuerzaTiroAtaque*fuerzaTiroAtaque + fuerzaTiroAtaque)/2 - DISTANCIA_RECORRIDO_CONTROLABLE;

                    int jugadorMasCercanoAPelotaFuerzaI = -1;
                    double distanciaMasCorta = 1000;
                    Iterator<Integer> it = listasDefensasPorfuerza[grupoFuerza].iterator();
                    while (it.hasNext()) {
                        int numJugador = it.next();
                        double distanciaPelota = General.getDistancia(
                            getPosicionJugador(numJugador).getX(), getPosicionJugador(numJugador).getX(),
                            xPelota, yPelota);
                        if (distanciaPelota < distanciaMasCorta) {
                            distanciaMasCorta = distanciaPelota;
                            jugadorMasCercanoAPelotaFuerzaI = numJugador;
                        }
                    }
                    
                    List<Double> angulosUtilizados = new ArrayList();
                    
                    // Ya tengo el jugador más cercano -> lo movilizo
                    if (jugadorMasCercanoAPelotaFuerzaI >= 0) {
                        if (distanciaAltoRiesgoAPorteria > 0) {
                            double angulo = General.getAngulo(xPelota, yPelota, LIMITE_X_MIN, 0);
                            double x = LIMITE_X_MIN - Math.cos(angulo) * distanciaAltoRiesgoAPorteria;
                            double y = 0 - Math.sin(angulo) * distanciaAltoRiesgoAPorteria;
                            PrediccionJugador prediccion = new PrediccionJugador(jugadorMasCercanoAPelotaFuerzaI,
                                    getVelocidad(jugadorMasCercanoAPelotaFuerzaI),
                                    getPosicionJugador(jugadorMasCercanoAPelotaFuerzaI).getX(),getPosicionJugador(jugadorMasCercanoAPelotaFuerzaI).getY());
                            prediccion.setPuntoIntercepcionPelota(new Point((int) Math.round(x), (int) Math.round(y)));
                            prediccionColocacionJugadores.add(prediccion);
                            setJugadorConOrden(jugadorMasCercanoAPelotaFuerzaI);
                            angulosUtilizados.add(angulo);
                        }
                    }

                    // Movilizo a los demás jugadores
                    it = listasDefensasPorfuerza[grupoFuerza].iterator();
                    while (it.hasNext()) {
                        int numJugador = it.next();
                        if (numJugador != jugadorMasCercanoAPelotaFuerzaI) {
                            if (distanciaAltoRiesgoAPorteria > 0) {
                                double angulo = General.getAngulo(getPosicionJugador(numJugador).getX(), getPosicionJugador(numJugador).getY(), LIMITE_X_MIN, 0);
                                //angulo = buscarMejorAngulo(angulo, angulosUtilizados, ANGULO_SEPARACION_DEFENSAS, Math.PI);
                                double x = LIMITE_X_MIN - Math.cos(angulo) * distanciaAltoRiesgoAPorteria;
                                double y = 0 - Math.sin(angulo) * distanciaAltoRiesgoAPorteria;
                                PrediccionJugador prediccion = new PrediccionJugador(numJugador,
                                        getVelocidad(numJugador),
                                        getPosicionJugador(numJugador).getX(),getPosicionJugador(numJugador).getY());
                                prediccion.setPuntoIntercepcionPelota(new Point((int) Math.round(x), (int) Math.round(y)));
                                prediccionColocacionJugadores.add(prediccion);
                                setJugadorConOrden(numJugador);
                                angulosUtilizados.add(angulo);
                            }
                        }
                    }
                }
            }
            return prediccionColocacionJugadores;
        }
            
        /**
         * Calcula la posición de un jugador para prepararle al siguiente pase
         */
        private List<PrediccionJugador> calculaColocacionIntercepcionSiguientePase(List<PrediccionJugador> prediccionColocacionJugadores) {
            List<PrediccionJugador> jugadoresMasAccesiblesAPelota = this.getPrediccionesTacticas();
            boolean segurosDeLlegar = false;
            int numeroJugadoresQueLleganSeguro = 0;
            List<PrediccionJugador> jugadoresQueVanAPelota = new ArrayList(NUM_JUGADORES);
            
            ////////////////////////////////////////////////////////////////////7
            //for (boolean jugadorConOrden: jugadoresConOrdenes) System.out.println("Jugador con ordenes: "+jugadorConOrden);
            ////////////////////////////////////////////////////////////////////7
            boolean encontrado = false;
            for (int i=0; (i< jugadoresMasAccesiblesAPelota.size()) && !encontrado; i++) {
                //for (PrediccionJugador jugadorQueVaAPelota: jugadoresQueVanAPelota) {
                PrediccionJugador jugadorQueVaAPelota = jugadoresMasAccesiblesAPelota.get(i);
                Point puntoIntercepcion = jugadorQueVaAPelota.getPuntoIntercepcionPelota();
                double angulo = General.getAngulo(puntoIntercepcion.getX(), puntoIntercepcion.getY(),LIMITE_X_MAX,0);
                
                int numeroJugadorAMovilizar = getJugadorMasCercanoPorDelante(puntoIntercepcion.getX(), puntoIntercepcion.getY(), getJugadoresConOrdenes());
                if (numeroJugadorAMovilizar != -1) {
                    PrediccionJugador prediccion = new PrediccionJugador(numeroJugadorAMovilizar,getVelocidad(numeroJugadorAMovilizar),getPosicionJugador(numeroJugadorAMovilizar).getX(),getPosicionJugador(numeroJugadorAMovilizar).getY());
                    double distanciaJugadorPorteria = General.getDistancia(getPosicionJugador(numeroJugadorAMovilizar).getX(),getPosicionJugador(numeroJugadorAMovilizar).getY(), LIMITE_X_MAX, 0);
                    double anguloCorregido =
                            (angulo > LIMITE_ANGULO_PARA_TIRO_SUP)?LIMITE_ANGULO_PARA_TIRO_SUP:angulo;
                    anguloCorregido =
                            (angulo < LIMITE_ANGULO_PARA_TIRO_INF)?LIMITE_ANGULO_PARA_TIRO_INF:angulo;
                    
                    double x2 = LIMITE_X_MAX - Math.cos(anguloCorregido) * distanciaJugadorPorteria;
                    double y2 = 0 - Math.sin(anguloCorregido) * distanciaJugadorPorteria;
                    prediccion.setPuntoIntercepcionPelota(new Point((int) Math.round(x2), (int) Math.round(y2)));
                    prediccionColocacionJugadores.add(prediccion);
                    //System.out.println("JUGADOR CON ORDEN ITC-> " + numeroJugadorAMovilizar);
                    setJugadorConOrden(numeroJugadorAMovilizar);
                    encontrado = true;
                }
            }
            return prediccionColocacionJugadores;
        }
        
        /**
         * Devuelve le jugador más cercano a la posición que esté hacia la portería. Excluyendo los <code>jugadoresNoContar</code>
         *
         * @param x X
         * @param y Y
         * @param jugadoresNoContar Jugadores con los que no hay que contar
         * @return Jugador más cercano o "-1" si no se ha encontrado.
         */
        private int getJugadorMasCercanoPorDelante(double x, double y, boolean[] jugadoresNoContar) {
            int jugadorMasCercano = -1;
            double distanciaAPosicion = 1000;
            for (int i=0; i<NUM_JUGADORES; i++) {
                if (!jugadoresNoContar[i]) {
                    Point posicionJugador = getPosicionJugador(i);
                    double distancia = General.getDistancia(x,y,posicionJugador.getX(),posicionJugador.getY());
                    double distanciaPuntoPorteria = General.getDistancia(x, y, LIMITE_X_MAX, 0);
                    double distanciaJugadorPorteria = General.getDistancia(posicionJugador.getX(),posicionJugador.getY(), LIMITE_X_MAX, 0);
                    if ((distancia < distanciaAPosicion) && (distanciaPuntoPorteria > distanciaJugadorPorteria)) {
                        jugadorMasCercano = i;
                        distanciaAPosicion = distancia;
                    }
                }
            }
            return jugadorMasCercano;
        }
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        public int getTipoJugador(int jugador) {
            return (int) estadoInicial[jugador][3];            
        }
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        public double getVelocidad(int jugador) {
            return (10 - estadoInicial[jugador][2]);
        }
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        public double getFuerza(int jugador) {
            return (estadoInicial[jugador][2]);
        }
        
        /**
         *Devuelve X inicial de un jugador
         *@return x
         */
        public double getXInicial(int jugador) {
            return (estadoInicial[jugador][0]);
        }
        
        /**
         *Devuelve Y inicial de un jugador
         *@return y
         */
        public double getYInicial(int jugador) {
            return (estadoInicial[jugador][1]);
        }
    }
    
    
    /**
     * Clase que define el equipo contrario
     */
    private class OtroEquipo extends UnEquipo {
        
        /** Flag para anular el 1er turno */
        boolean primerTurno = true;
        
        /** Posiciones en el movimiento anterior */
        private int posicionesAnteriores[][]={
            {0,0},  // Jugador 1[0]
            {0,0},  // Jugador 2[1]
            {0,0},  // Jugador 3[2]
            {0,0},  // Jugador 4[3]
            {0,0},  // Jugador 5[4]
            {0,0},  // Jugador 6[5]
            {0,0},  // Jugador 7[6]
            {0,0},  // Jugador 8[7]
            {0,0},  // Jugador 9[8]
            {0,0},  // Jugador 10[9]
            {0,0}  // Jugador 11[10]
        };
        
        /** Estimación de fuerza, inicialmente estimamos 5 para cada jugador */
        private int estimacionFuerza[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
        
        /** Estimación de fuerza, inicialmente estimamos 5 para cada jugador */
        private boolean estimacionFuerzaEsReal[] = {false, false, false, false, false, false, false, false, false, false, false};
        
        public OtroEquipo() {
            super();
        }
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        public double getVelocidad(int jugador) {
            if (getFuerza(jugador) < 0) return 7;
            return (10 - estimacionFuerza[jugador]);
        }
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        public double getFuerza(int jugador) {
            return (estimacionFuerza[jugador] < 0)?5:estimacionFuerza[jugador];
        }
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        private double getXAnterior(int jugador) {
            return posicionesAnteriores[jugador][0];
        }
        
        /**
         *Devuelve la velocidad de un jugador
         *@return velocidad del jugador
         */
        private double getYAnterior(int jugador) {
            return posicionesAnteriores[jugador][1];
        }
        
        /**
         *Actualiza la tabla de posiciones anteriores de los jugadores
         */
        private void actualizarPosicionesAnteriores(Point[] equipo) {
            for (int i=0; i<NUM_JUGADORES; i++) {
                posicionesAnteriores[i][0] = (int) equipo[i].getX();
                posicionesAnteriores[i][1] = (int) equipo[i].getY();
            }
        }
        
        /**
         * Calcula la fuerza de cada jugador en función de sus movimientos
         */
        private void calcularFuerza(Point[] equipo) {
            for (int i=0; i<NUM_JUGADORES; i++) {
                if (! estimacionFuerzaEsReal[i]) { // Si todavía no estamos seguros de su fuerza
                    int xAnt = posicionesAnteriores[i][0];
                    int x = (int) equipo[i].getX();
                    int yAnt = posicionesAnteriores[i][1];
                    int y = (int) equipo[i].getY();
                    
                    if ((x != xAnt) || (y != yAnt)) { // Si se ha movido, calculamos la fuerza
                        
                        int d = (int) General.getDistancia(x,y,xAnt,yAnt);
                        if (d > 10) // Situación de recolocación
                            break;
                        int estimacionFuerzaAux = 10-d;
                        estimacionFuerzaEsReal[i] = (estimacionFuerzaAux == estimacionFuerza[i]);
                        estimacionFuerza[i] = estimacionFuerzaAux;
                    }
                }
            }
            
        }
        
        /**
         * Actualiza el estado del equipo
         */
        public void actualizarEquipo(Partido partido, Point[] equipo) {
            super.actualizarEquipo(partido, equipo);
            if (primerTurno) {
                primerTurno = false;
            } else {
                calcularFuerza(equipo);
            }
            actualizarPosicionesAnteriores(equipo);
        }
        
        public void imprimirFuerza() {
            for (int i=0; i<NUM_JUGADORES; i++) {
                System.out.print(" | Jugador "+i+" ("+estimacionFuerzaEsReal[i]+") -> "+getFuerza(i));
            }
            System.out.println();
        }
        
    }
    
    
    /**
     * Clase que procesa los movimientos de la pelota
     */
    private class Pelota {
        
        public static final int NUM_PREDICCIONES = 30;
        
        private Point posicionInicialTiro = null;
        private Point posicionAnterior = null;
        private Point posicionActual;
        private double angulo = 0; // angulo del vector de la pelota
        private int tiempoPasado;
        
        private int velocidadBola = 0;
        private boolean ataque = true;
        
        private PrediccionPelota prediccionPelota[] = new PrediccionPelota[NUM_PREDICCIONES];
        
        public Pelota(){
            for (int i=0; i<prediccionPelota.length; i++)
                prediccionPelota[i] = new PrediccionPelota();
        }
        
        
        /**
         * Indica si la pelota va en dirección de nuestro campo
         *
         * @return Si va en dirección de nuestro campo.
         */
        public boolean esAtaque() {
            return ataque;
        }
        
        private void setEsAtaque(boolean ataque) {
            this.ataque = ataque;
        }
        
        /**
         * Actualiza el estado del equipo
         */
        public void actualizarPelota(Partido partido) {
            this.posicionActual = partido.getSituacionJugadores().getBola();
            calcularVelocidadYAnguloActualBola();
            calcularVectorPrediccionPelota();
        }
        
        /**
         * Comprueba si ha habido una correccion de las coordenadas en
         * plan "cutre".
         */
        protected boolean coordenadasCorregidasAlLimite(int x, int y) {
            return ((x==LIMITE_X_MAX) || (x==LIMITE_X_MIN) || (y==120) || (y==-120));
        }
        
        
        /**
         * Devuelve la velocidad actual de la bola
         */
        protected void calcularVelocidadYAnguloActualBola() {
            incrementarTiempoPasado();
            double angulo;
            int velocidad;
            if (getPosicionInicialTiro() == null) {
                setPosicionInicialTiro(getPosicionActual());
                setPosicionAnterior(getPosicionActual());
                resetearTiempoPasado();
                velocidad = 0;
                angulo = 0;
            } else {
                double x0 = getPosicionInicialTiro().getX();
                double y0 = getPosicionInicialTiro().getY();
                double x1 = getPosicionActual().getX();
                double y1 = getPosicionActual().getY();
                if (coordenadasCorregidasAlLimite((int) x1, (int) y1)) {
                    angulo = getAngulo();
                    Trayectoria trayectoria = corregirTrayectoria((int) x1, (int) y1, angulo);
                    angulo = trayectoria.angulo;
                    if (getVelocidad() > 0)
                        velocidad = getVelocidad() - 1;
                    else
                        velocidad = 0;
                    setPosicionInicialTiro(getPosicionActual());
                    setPosicionAnterior(getPosicionActual());
                    resetearTiempoPasado();
                } else {
                    int xAnt = (int) getPosicionAnterior().getX();
                    int yAnt = (int) getPosicionAnterior().getY();
                    
                    int velocidadAnterior = getVelocidad();
                    double distanciaUltimoPunto = General.getDistancia(xAnt,yAnt,x1,y1);
                    int velocidadUltimoPunto = (int) Math.round(distanciaUltimoPunto);
                    
                    double anguloAnterior = getAngulo();
                    double nuevoAngulo = General.getAngulo(xAnt,yAnt,x1,y1);
                    
                    // Compruebo si es un nuevo tiro
                    boolean nuevoTiro =
                            (Math.abs(velocidadUltimoPunto - velocidadAnterior) > 3) ||
                            (Math.abs(anguloAnterior - nuevoAngulo) > 0.75);
                    
                    //System.out.println("ANG:" +anguloAnterior + " - "+nuevoAngulo);
                    //System.out.println("VEL:" +velocidadUltimoPunto + " - "+velocidadAnterior);
                    //System.out.println("CAMBIO:" +nuevoTiro);
                    
                    if (nuevoTiro) {
                        velocidad = velocidadUltimoPunto;
                        angulo = nuevoAngulo;
                        setPosicionInicialTiro(getPosicionAnterior());
                        setPosicionAnterior(getPosicionActual());
                        resetearTiempoPasado();
                        incrementarTiempoPasado(); // Incremento pués estamos en t1 (no en t0)
                    } else  {
                        angulo = General.getAngulo(x0,y0,x1,y1);
                        
                        double xConVelocidadAnt = xAnt + Math.round(Math.cos(angulo)*velocidadAnterior);
                        double yConVelocidadAnt = yAnt + Math.round(Math.sin(angulo)*velocidadAnterior);
                        
                        // Compruebo si la velocidad anterior era +/- correcta
                        if ((Math.abs(xConVelocidadAnt - x1)<=1) && (Math.abs(yConVelocidadAnt - y1)<=1)) {
                            if (getVelocidad() > 0)
                                velocidad = velocidadAnterior - 1;
                            else
                                velocidad = 0;
                        } else { // La velocidad anterior puede que no fuese exacta
                            velocidad = (int) Math.round(General.getDistancia(xAnt,yAnt,x1,y1));
                            //System.out.println("CAMBIO VELOCIDAD (COORD ANT): "+ xConVelocidadAnt + " - " + yConVelocidadAnt);
                            //System.out.println("CAMBIO VELOCIDAD (COORD CAL): "+ x1 + " - " + y1);
                            //System.out.println("VELOCIDAD: "+ (velocidadAnterior-1) + " por " + velocidad);
                        }
                        
                        setPosicionAnterior(getPosicionActual());
                        incrementarTiempoPasado();
                    }
                }
            }
            setEsAtaque((angulo > (-Math.PI/2)) && (angulo < (Math.PI/2)));
            setVelocidadBola(velocidad);
            setAngulo(angulo);
        }
        
        protected int getTiempoPasado() {
            return tiempoPasado;
        }
        
        protected void resetearTiempoPasado() {
            tiempoPasado = 0;
        }
        
        protected void incrementarTiempoPasado() {
            tiempoPasado++;
        }
        
        protected void setPosicionInicialTiro(Point pto) {
            posicionInicialTiro = pto;
        }
        protected Point getPosicionInicialTiro() {
            return posicionInicialTiro;
        }
        public Point getPosicionAnterior() {
            return posicionAnterior;
        }
        protected void setPosicionAnterior(Point pto) {
            posicionAnterior = pto;
        }
        
        protected void setAngulo(double angulo) {
            this.angulo = angulo;
        }
        
        protected double getAngulo() {
            return angulo;
        }
        
        public Point getPosicionActual() {
            return posicionActual;
        }
        protected void setPosicionActual(Point pto) {
            posicionActual = pto;
        }
        
        protected void setVelocidadBola(int velocidad) {
            this.velocidadBola = velocidad;
        }
        
        protected void calcularVectorPrediccionPelota() {
            double x0 = posicionActual.getX();
            double y0 = posicionActual.getY();
            double anguloPrediccion = getAngulo();
            //int velocidadPelotaIteracion = getVelocidad();
            int velocidadPelota = getVelocidad();
            
            prediccionPelota[0].setCoordenadas((Point) posicionActual.clone());
            prediccionPelota[0].setVelocidad(velocidadPelota);
            boolean esGol = false;
            for (int i=1; i<NUM_PREDICCIONES; i++) {
                double x;
                double y;
                velocidadPelota = (velocidadPelota!=0)?(velocidadPelota-1):0;
                x = x0 + Math.round(Math.cos(anguloPrediccion)*velocidadPelota);
                y = y0 + Math.round(Math.sin(anguloPrediccion)*velocidadPelota);
                
                x0 = x;
                y0 = y;
                
                Trayectoria trayectoria = corregirTrayectoria(x, y, anguloPrediccion);
                x = trayectoria.x;
                y = trayectoria.y;
                anguloPrediccion = trayectoria.angulo;
                
                // Si es gol "paro" la pelota
                if (esGol((int)x,(int)y)) {
                    velocidadPelota = 0;
                }
                
                prediccionPelota[i].setCoordenadas(new Point((int) x, (int)y));
                prediccionPelota[i].setVelocidad(velocidadPelota);
            }
        }
        
        public int getVelocidad() {
            return velocidadBola;
        }
        
        public PrediccionPelota[] getPrediccionPelota() {
            return prediccionPelota;
        }
        
        public int getXActual(){
            return (int) posicionActual.getX();
        }
        
        public int getYActual(){
            return (int) posicionActual.getY();
        }
        
    }
    
    /**
     * Clase con la información ampliada del partido
     */
    private class Partido {
        
        /** Datos de mi equipo */
        private MiEquipo miEquipo = new MiEquipo();
        
        /** Datos del otro equipo */
        private OtroEquipo otroEquipo = new OtroEquipo();
        
        /** Datos de la pelota */
        private Pelota pelota = new Pelota();
        
        private SituacionJugadores sj;
        
        public void actualizarPartido(SituacionJugadores sj) {
            this.sj = sj;
            otroEquipo.actualizarEquipo(this, sj.getContrario());
            pelota.actualizarPelota(this);
            miEquipo.actualizarEquipo(this, sj.getMisJugadores());
        }
        
        public SituacionJugadores getSituacionJugadores() {
            return sj;
        }
        
        public Pelota getPelota() {
            return pelota;
        }
        
        public OtroEquipo getOtroEquipo() {
            return otroEquipo;
        }
        
        public MiEquipo getMiEquipo() {
            return miEquipo;
        }
        
    }
    
}
